其他
原创 | Mybatis-plus框架常见SQL注入场景
关于Mybatis-plus
常见SQL注入场景
与SpringDataJpa类似,mybatis-plus提供了相关的funciton进行sql的操作,例如like("name","tks")——>name like '%tks%',同时也很贴心的考虑到了SQL注入问题,对绝大部分场景进行了预编译处理。但是类似动态表名、orderby这种需要拼接的场景在实际开发中还是需要额外的注意。
2.1 条件构造器Wrapper
条件构造器Wrapper可以用于复杂的数据库操作:大于、小于、模糊查询等等。
比较常用的是QueryWrapper和UpdateWrapper:
Wrapper :条件构造抽象类,最顶端父类,抽象类中提供4个方法 AbstractWrapper :用于查询条件封装,生成 sql 的 where 条件 AbstractLambdaWrapper :Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。 LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper LambdaUpdateWrapper :Lambda 更新封装Wrapper QueryWrapper :Entity 对象封装操作类,不是用lambda语法 UpdateWrapper :Update 条件封装,用于Entity对象更新操作
2.1.1 配置方法
首先配置mapper:
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.samples.wrapper.entity.User;
public interface UserMapper extends BaseMapper<User> {
}
然后直接调用相关的api进行操作即可,例如查询name为“admin”的用户:QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name","admin")
userMapper.selectOne(wrapper); // 查询一个数据。查询多个使用List或者Map
like模糊查询需要在mapperxml配置中用sql的内置函数进行拼接,拼接后再采用#预编译的方式进行查询:
<select id="searchUser" parameterType="String" resultType="com.codeaudit.sqlinject.mybatis.User">
select * from user where name like '%'||'#param#'||'%' #Oracle
select * from user where name like CONCAT('%',#param#,'%') #mysql
select * from user where name like '%'+#param#+'%' #mssql
</select>
<select id="searchUser" parameterType="String" resultType="com.tk.codeAudit.sqlinject.mybatis.User">
select * from user where id in
<foreach collection="array" index="index" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
like模糊查询
QueryWrapper<User> qw = new QueryWrapper<>();
qw.select("id","name").like("name", "jack");
List<User> plainUsers = userMapper.selectList(qw);
in范围查询
QueryWrapper<User> qw = new QueryWrapper<>();
qw.select("id","name").in("age","10","30");
List<User> plainUsers = userMapper.selectList(qw);
2.1.2 常见注入场景
PS:
boolean condition
参数表示该条件是否加入最后生成的sql中如下是审计过程中发现的比较常见的存在注入风险的点,也是对官方文档的一个补充:
apply
该方法可用于数据库函数的调用:
apply(String applySql, Object... params)
apply(boolean condition, String applySql, Object... params)
apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")--->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
注意:动态入参的params
对应前面applySql
内部的{index}
部分。这样可以进行预编译,防止SQL注入问题。
例如下面的例子,未通过动态入参params进行预编译处理:
List<User> plainUsers4 = userMapper.selectList(new QueryWrapper<User>()
.apply("role_id = 2"));
List<User> lambdaUsers4 = userMapper.selectList(new QueryWrapper<User>().lambda()
.apply("role_id = 2"));
使用params进行预编译处理:
List<User> plainUsers5 = userMapper.selectList(new QueryWrapper<User>()
.apply("role_id = {0}",2));
List<User> lambdaUsers5 = userMapper.selectList(new QueryWrapper<User>().lambda()
.apply("role_id = {0}",2));
last
last(String lastSql)
last(boolean condition, String lastSql)
.last("order by " + StringUtil.isEmpty(queryParam.getColumns()) + " " + queryParam.getSort()));
exists/notExists
这两个场景同样的是进行sql的拼接,如果相关内容用户可控,那么会存在Sql注入风险:
拼接 EXISTS ( sql语句 )
exists(String existsSql)
exists(boolean condition, String existsSql)
拼接 NOT EXISTS ( sql语句 )
notExists(String notExistsSql)
notExists(boolean condition, String notExistsSql)
QueryWrapper<User> qw = new QueryWrapper<>();
qw.select("id","name").exists("select id from table where role_id = 2");
having
having(String sqlHaving, Object... params)
having(boolean condition, String sqlHaving, Object... params)
params
对应前面applySql
内部的{index}
部分。这样可以进行预编译防止SQL注入问题。例如如下的例子:
QueryWrapper<User>().select("id","name").groupBy("name").having(true, "role_id>{0}",2));
Order By
原始orderby
orderBy(boolean condition, boolean isAsc, R... columns)
ORDER BY 字段, ... DESC
orderByDesc(R... columns)
orderByDesc(boolean condition, R... columns)
ORDER BY 字段, ... ASC
orderByDesc(R... columns)
orderByDesc(boolean condition, R... columns)
例如如下H2 database的例子:
QueryWrapper<User> qw = new QueryWrapper<>();
qw.select("id","name").orderByAsc(str);
group By
groupBy(R... columns)
groupBy(boolean condition, R... columns)
QueryWrapper<User> qw = new QueryWrapper<>();
qw.select("id","name").groupBy(str);
insql/notinsql
通过sql 注入方式调用in/not in 方法:
notInSql
notInSql(boolean condition, R column, String inValue);
notInSql(R column, String inValue)
inSql
inSql(R column, String inValue)
inSql(boolean condition, R column, String inValue)
userMapper.selectList(new QueryWrapper<User>().inSql(str, "select id from role where id = 2"));
str用户可控,那么这里尝试带外请求dnslog,成功接收到带外请求:
同理,除了column字段以外,inValue字段可控的情况下也会存在注入风险。
2.2 使用 Wrapper 自定义SQL(特殊的预编译场景)
虽然mybatis-plus提供了很多函数使用,但是不一定能满足所有的业务需要,此时Wrapper提供了自定义SQL场景,虽然跟传统的mybatis一样使用$进行注解,但是实际上ew已经做了预编译处理。同样的也支持注解&xml配置。
注解方式
@Select("select * from mysql_data ${ew.customSqlSegment}")
List<MysqlData> getAll(@Param(Constants.WRAPPER) Wrapper wrapper);
xml方式
<select id="getAll" resultType="MysqlData">
SELECT * FROM mysql_data ${ew.customSqlSegment}
</select>
2.3 其他
除此之外,官方文档中还推荐使用
com.github.pagehelper
进行分页处理:<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.5</version>
</dependency>
主要是setOrderBy(java.lang.String)方法
com.github.pagehelper.page.PageMethod
主要是startPage(int,int,java.lang.String)方法
com.github.pagehelper.PageHelper
主要是startPage(int,int,java.lang.String)方法
安全加固建议
https://www.cnblogs.com/nongzihong/p/12661446.html
原创 | 沙盒逃逸之seccomp学习
原创 | 堆的tcache利用
原创 | 堆的off-by-one利用
原创 | vulnhub: Momentum:1